﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows;
using System.Windows.Threading;
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using OpenCvTest;

namespace QuizRecognition
{
    /// <summary>
    ///     Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void UpdateImages()
        {
            itemsView.ItemsSource = GetProcessingStages().Select(x => x.ToBitmapSource()).ToArray();
            _updateScheduled = false;
        }

        private IEnumerable<IImage> GetProcessingStages()
        {
            var image = new Image<Bgr, byte>(@"sample_data\400d_.jpg").PyrDown().PyrDown().PyrDown().PyrUp();
            //yield return image.Clone();

            //var gray = image.Convert<Gray, byte>();
            //var gray = image.InRange(new Bgr(0, 0, 0), new Bgr(15, 90, 90));
            //yield return gray;

            var canny = image.Canny(slider1.Value, slider2.Value);
            yield return canny;

            var rootContour = canny.FindContours(CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE, RETR_TYPE.CV_RETR_TREE);
            var biggestContours = EnumContours(rootContour).OrderByDescending(c => c.Area).Distinct();
            var biggestContour = biggestContours.First();
            if (biggestContour.Area < 140000)
            {
                Title = "Outer Contour Not Found!!";
            }
            else
            {
                Title = "Outer contour found, Area = " + biggestContour.Area;
            }
            //foreach (var contour in EnumContours(rootContour))
            //{
            //    image.Draw(contour, new Bgr(Color.Blue), 1);
            //}
            //yield return image;

            // Now extract largest contour
            var box = biggestContour.GetMinAreaRect();
            var tableImage = image.Copy(box);
            yield return tableImage;

            // Now extract 3rd & 4rd column and trim header (7.8%)
            //MCvBox2D x = new MCvBox2D();
            var w = tableImage.Width;
            var h = tableImage.Height;
            var headerSize = (int) (h*0.078);
            var left = (int) (w/4*1.75);
            var croppedColumn = tableImage.Copy(new Rectangle(left, headerSize, w-left, h - headerSize));

            // TODO:!!! See StopSignDetector! We could use MatchShapes to find + and - shapes, and that's it! Group them by coordinates and we are done.
            var markContours = croppedColumn.Canny(slider1.Value, slider2.Value).FindContours(CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE, RETR_TYPE.CV_RETR_TREE);
            foreach (var contour in EnumContours(markContours)
                .Where(c => c.Area > 5)
                .Select(c => new {c, Rect = c.GetMinAreaRect().MinAreaRect()})
                //.Where(c => (double) c.Rect.Width/c.Rect.Height < 2 && (double) c.Rect.Width/c.Rect.Height > 0.5 && c.Rect.Width < croppedColumn.Width/4 && c.Rect.Height < croppedColumn.Height/15)
                .Select(c => c.c))
            {
                croppedColumn.Draw(contour, new Bgr(Color.Blue), 1);
            }

            yield return croppedColumn;

        }

        private void Slider_OnValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            if (_updateScheduled)
                return;

            _updateScheduled = true;
            Dispatcher.BeginInvoke((Action) (UpdateImages), DispatcherPriority.SystemIdle);
        }

        private IEnumerable<Contour<System.Drawing.Point>> EnumContours(Contour<System.Drawing.Point> root)
        {
            for (; root != null; root = root.HNext)
            {
                yield return root;
                foreach (var child in EnumContours(root.VNext))
                    yield return child;
            }
        }

        private bool _updateScheduled;
    }
}